import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import jade.content.Concept;
import jade.content.ContentElement;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.basic.Action;
import jade.content.onto.basic.Result;
import jade.core.AID;
import jade.core.Runtime;
import jade.core.Agent;
import jade.core.Location;
import jade.core.ProfileImpl;
import jade.core.behaviours.Behaviour;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.ParallelBehaviour;
import jade.core.behaviours.SequentialBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.domain.JADEAgentManagement.KillAgent;
import jade.domain.JADEAgentManagement.QueryPlatformLocationsAction;
import jade.domain.mobility.CloneAction;
import jade.domain.mobility.MobileAgentDescription;
import jade.domain.mobility.MobilityOntology;
import jade.domain.mobility.MoveAction;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.lang.acl.UnreadableException;
import jade.proto.states.MsgReceiver;
import jade.wrapper.AgentContainer;
import jade.wrapper.ControllerException;
import jade.wrapper.StaleProxyException;

public class CuratorAgent extends Agent {

	private LinkedList<Thingy> items = new LinkedList<Thingy>();
	private LinkedList<Thingy> itemsSold = new LinkedList<Thingy>();
	private ArrayList<AID> bidders = new ArrayList<AID>(10);
	private ArrayList<AID> stillBidders = new ArrayList<AID>(10);
	private int currentItem = 0;
	private int STARTPRICE = 500;
	private boolean bidAccepted;
	private long time;
	private int DEC = 50;
	private int DELAY = 100;
	private int homei = 0;
	ACLMessage superoutMsg = new ACLMessage(ACLMessage.PROXY);

	transient private AgentContainer[] home = new AgentContainer[2];

	private Location destination;
	private Map<String, Location> locations = new HashMap<String, Location>();

	protected void afterClone() {
		init();
	}

	protected void setup() {

		items.add(new Thingy("Hard rock", "Nature", 500));
		items.add(new Thingy("Mona Lisa", "Painting", 500));
		items.add(new Thingy("Skriiet", "Painting", 500));
		items.add(new Thingy("Fine picture", "Painting", 300));

		// Register language and ontology
		getContentManager().registerLanguage(new SLCodec());
		getContentManager().registerOntology(MobilityOntology.getInstance());

		// Create the container objects
		home[0] = Runtime.instance().createAgentContainer(new ProfileImpl());
		home[1] = Runtime.instance().createAgentContainer(new ProfileImpl());

		// fetch locations

		try {
			// Get available locations with AMS
			sendRequest(new Action(getAMS(), new QueryPlatformLocationsAction()));

			// Receive response from AMS
			MessageTemplate mt = MessageTemplate.and(
					MessageTemplate.MatchSender(getAMS()),
					MessageTemplate.MatchPerformative(ACLMessage.INFORM));
			ACLMessage resp = blockingReceive(mt);

			// Get locations from AMS from contentmanager
			ContentElement ce = getContentManager().extractContent(resp);
			Result result = (Result) ce;
			jade.util.leap.Iterator it = result.getItems().iterator();

			// add locationname to our hashmap
			while (it.hasNext()) {
				Location loc = (Location) it.next();
				locations.put(loc.getName(), loc);
				System.out.println(loc.getName());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		addBehaviour(new ReceiveCommands(this));

		// doWait(2000);

		// create clones and move them
		try {
			createCloneAndMove(getAID(), home[0].getContainerName(), 1);
			createCloneAndMove(getAID(), home[1].getContainerName(), 2);
		} catch (ControllerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// create 2 profilers which will clone themselves

		Object[] args = new Object[2];
		for (int i = 0; i < 2; i++) {
			args[0] = getAID();
			String name = "Profiler" + (i + 1);
			try {
				home[i].createNewAgent(name, newTestProfiler.class.getName(),
						args).start();
			} catch (StaleProxyException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		//receive proposals and determine best when curators get back
		this.addBehaviour(new CyclicBehaviour() {

			@Override
			public void action() {
				ACLMessage inMsg = null;

				int bestBid[] = new int[2];

				inMsg = myAgent.receive(MessageTemplate
						.MatchPerformative(ACLMessage.CONFIRM));
				if (inMsg != null) {
					homei++;
					System.out.println("Server got msg " + homei + " from "
							+ inMsg.getSender());
					superoutMsg.addReceiver(inMsg.getSender());
					// if(homei<2){
					// System.out.println("SEnder "+inMsg.getSender());

					inMsg = null;
					// }
					if (homei >= 2) {
						System.out.println("Server got 2 msgs");
						// System.out.println(items.size());

						for (int i = 0; i < items.size(); i++) {

							System.out.println(items.get(i).name);
							superoutMsg.setContent(items.get(i).name);
							// outMsg.setPerformative(ACLMessage.UNKNOWN);

							// System.out.println("sending hte shit");
							send(superoutMsg);
							// System.out.println("msg was sent" +superoutMsg);

							for (int j = 0; j < 2; j++) {
								inMsg = null;
								inMsg = blockingReceive(MessageTemplate
										.MatchPerformative(ACLMessage.AGREE));
								// System.out.println("msg was recived");
								bestBid[j] = Integer.parseInt(inMsg
										.getContent());
							}
							if (bestBid[1] <= bestBid[0])
								System.out.println("best bid on "
										+ items.get(i).name + " was "
										+ bestBid[0]);
							else if (bestBid[0] < bestBid[1])
								System.out
										.println("best bid was " + bestBid[1]);
							else {
								System.out.println("mattafakka");
							}
						}
					}
				}
				inMsg = null;

			}

		});

	}

	// method that creates clone
	private void createCloneAndMove(AID aid, String destName, int id) {
		// set location
		Location dest = (Location) locations.get(destName);

		// set mobile agent description
		MobileAgentDescription mad = new MobileAgentDescription();
		mad.setName(aid);
		mad.setDestination(dest);
		CloneAction ca = new CloneAction();
		ca.setNewName("Clone-" + aid.getLocalName() + id);
		ca.setMobileAgentDescription(mad);

		// sends a request about making a new copy of this agent
		sendRequest(new Action(getAID(), ca));
	}

	// Moves to agent to the destination
	private void moveAgent(AID aid, String destName) {

		// setlocation
		Location dest = (Location) locations.get(destName);

		// set mobile agent description
		MobileAgentDescription mad = new MobileAgentDescription();
		mad.setName(aid);
		mad.setDestination(dest);
		MoveAction ma = new MoveAction();
		ma.setMobileAgentDescription(mad);
		System.out.println(aid + " " + ma + " " + dest);
		sendRequest(new Action(aid, ma));
	}
	
	//receive command for actions
	class ReceiveCommands extends CyclicBehaviour {
		// -----------------------------------------------
		private Location destination;

		ReceiveCommands(Agent a) {
			super(a);
		}

		public void action() {

			ACLMessage msg = receive(MessageTemplate
					.MatchPerformative(ACLMessage.REQUEST));

			if (msg == null) {
				block();
				return;
			}

			if (msg.getPerformative() == ACLMessage.REQUEST) {

				try {
					ContentElement content = getContentManager()
							.extractContent(msg);
					Concept concept = ((Action) content).getAction();

					if (concept instanceof CloneAction) {

						CloneAction ca = (CloneAction) concept;
						String newName = ca.getNewName();
						Location l = ca.getMobileAgentDescription()
								.getDestination();
						if (l != null)
							destination = l;
						doClone(destination, newName);
					} else if (concept instanceof MoveAction) {

						MoveAction ma = (MoveAction) concept;
						Location l = ma.getMobileAgentDescription()
								.getDestination();
						if (l != null)
							doMove(destination = l);
					} else if (concept instanceof KillAgent) {

						doDelete();
					}
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			} else {
				System.out.println("Unexpected msg from controller agent");
			}
		}
	}

	/*
	 * private void createCloneAndMove(AID aid, String destName, int id) { //
	 * set location Location dest = (Location) locations.get(destName);
	 * 
	 * // set mobile agent description MobileAgentDescription mad = new
	 * MobileAgentDescription(); mad.setName(aid); mad.setDestination(dest);
	 * CloneAction ca = new CloneAction(); ca.setNewName("Clone-" +
	 * aid.getLocalName() + id); ca.setMobileAgentDescription(mad);
	 * 
	 * // sends a request about making a new copy of this agent sendRequest(new
	 * Action(getAID(), ca)); }
	 */
	//code to run on clones
	protected void init() {
		// Register the Curator service in the yellow pages
		System.out.println(getAID().getName() + " is now running");
		DFAgentDescription dfd = new DFAgentDescription();
		dfd.setName(getAID());
		ServiceDescription sd = new ServiceDescription();
		sd.setType("Curator");
		sd.setName("Curator");
		dfd.addServices(sd);
		try {
			DFService.register(this, dfd);
		} catch (FIPAException fe) {
			fe.printStackTrace();
		}

		/*
		 * items.add(new Thingy("Hard rock","Nature",500)); items.add(new
		 * Thingy("Mona Lisa","Painting",500)); items.add(new
		 * Thingy("Skriiet","Painting",500)); items.add(new
		 * Thingy("Fine picture","Painting",300));
		 */

		ParallelBehaviour pb = new ParallelBehaviour();

		// Looping SequentialBehaviour
		SequentialBehaviour seq = new SequentialBehaviour() {
			public int onEnd() {
				if (!items.isEmpty()) {
					reset();
					myAgent.addBehaviour(this);
				}
				return super.onEnd();

			}
		};

		pb.addSubBehaviour(new Register());

		seq.addSubBehaviour(new StartAuction());
		seq.addSubBehaviour(new MakeAgreement(this, DELAY));

		pb.addSubBehaviour(seq);
		this.addBehaviour(pb);

	}

	private class Register extends MsgReceiver {

		@Override
		public void action() {
			MessageTemplate mt = MessageTemplate
					.MatchPerformative(ACLMessage.SUBSCRIBE);
			ACLMessage msg = receive(mt);
			if (msg != null) {
				ACLMessage msga = new ACLMessage(ACLMessage.CONFIRM);
				send(msga);
				bidders.add(msg.getSender());
				System.out.println(myAgent.getLocalName() + " Bidder "
						+ msg.getSender().getName() + " subscribing");
			}

		}

	}

	private class StartAuction extends OneShotBehaviour {

		@Override
		public void action() {
			doWait(500);
			bidAccepted = false;
			ACLMessage msg = new ACLMessage(ACLMessage.INFORM);

			for (AID a : bidders)
				msg.addReceiver(a);

			msg.setContent(items.get(currentItem).name);
			send(msg);
			System.out.println("Bidding starts on "
					+ items.get(currentItem).name);
			stillBidders = bidders;
		}

	}
	
	//Makes agreemant on pricex
	private class MakeAgreement extends TickerBehaviour {
		public MakeAgreement(Agent a, long period) {
			super(a, period);
			// TODO Auto-generated constructor stub
		}

		public void onTick() {
			// make CFP
			getContentManager().registerLanguage(new SLCodec());
			getContentManager()
					.registerOntology(MobilityOntology.getInstance());
			items.get(0).price = items.get(0).price - DEC;
			bidAccepted = false;
			ACLMessage CFPmsg = new ACLMessage(ACLMessage.CFP);

			for (AID a : stillBidders)
				CFPmsg.addReceiver(a);
			try {
				CFPmsg.setContentObject(items.get(0));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			// CFPmsg.setContent(items.get(currentItem).name +
			// SEPARATOR+items.get(0).price);
			send(CFPmsg);
			System.out.println("Bid on " + items.get(currentItem).name
					+ " now just " + items.get(0).price);

			// Get bids
			MessageTemplate mt = MessageTemplate
					.MatchPerformative(ACLMessage.PROPOSE);

			ACLMessage inMsg;
			ACLMessage rMsg = new ACLMessage(ACLMessage.REJECT_PROPOSAL);
			ACLMessage aMsg = new ACLMessage(ACLMessage.ACCEPT_PROPOSAL);
			ACLMessage iMsg = new ACLMessage(ACLMessage.INFORM);

			time = System.currentTimeMillis();
			while (System.currentTimeMillis() < time + DELAY - (DELAY / 10)) {
				inMsg = myAgent.receive(mt);
				if (inMsg != null) {

					if (inMsg.getPerformative() == ACLMessage.PROPOSE
							&& bidAccepted == false) {
						bidAccepted = true;
						aMsg.addReceiver(inMsg.getSender());
						System.out.println(inMsg.getSender().getLocalName()
								+ " won the auction of " + items.get(0).name
								+ " , paid " + items.get(0).price);
						send(aMsg);
					} else if (inMsg.getPerformative() == ACLMessage.NOT_UNDERSTOOD) {
						stillBidders.remove(inMsg.getSender());
						System.out.println(inMsg.getSender() + " is out");
					} else if (inMsg.getPerformative() == ACLMessage.PROPOSE
							&& bidAccepted == true) {
						rMsg.addReceiver(inMsg.getSender());
						System.out.println(inMsg.getSender().getLocalName()
								+ " is too late");
						send(rMsg);
					} else {
						System.out.println(inMsg.getSender() + " is waiting");
					}
					inMsg = null;

				}

			}

			if (stillBidders.isEmpty() || items.get(0).price <= DEC) {
				// System.out.println(bidAccepted+""+stillBidders.isEmpty()+items.get(0).price);
				System.out.println("Noone bought item");
				iMsg.setContent("Auctioon over");
				send(iMsg);
				items.add(items.get(0));
				items.remove();
				items.get(0).price = STARTPRICE;
				this.stop();
			}
			if (bidAccepted) {
				System.out.println("Someone won item");
				itemsSold.add(items.get(currentItem));
				items.remove(currentItem);
				bidAccepted = false;
				this.stop();
			}
			if (bidAccepted && !items.isEmpty())
				items.get(0).price = STARTPRICE;
			if (items.isEmpty()) {
				this.goHome();
				this.stop();
			}
		}
		//Clone will go back to main.container to report it's prices
		private void goHome() {
			moveAgent(getAID(), "Main-Container");
			System.out.println("I'm going home");

			ACLMessage msg = new ACLMessage(ACLMessage.CONFIRM);
			ACLMessage reply = new ACLMessage(ACLMessage.PROXY);
			msg.addReceiver(new AID("curator", AID.ISLOCALNAME));
			msg.setContent("honey im home");
			send(msg);
			for (int k = 0; k < itemsSold.size(); k++) {
				// System.out.println("k="+k+"iSold="+itemsSold.size());

				reply = blockingReceive(MessageTemplate
						.MatchPerformative(ACLMessage.PROXY));
				// System.out.println("hej" + reply);

				int i = 0;
				// System.out.println("client : " + reply.getContent());
				for (; i < 4; i++) {

					if (reply.getContent().equals(itemsSold.get(i).name)) {
						// System.out.println("equals");
						break;
					}
				}

				msg.setContent("" + itemsSold.get(i).price);
				msg.setPerformative(ACLMessage.AGREE);
				send(msg);
			}
			// System.out.println("FRIHET");
		}
	}

	//sends a request for action
	void sendRequest(Action action) {
		// ---------------------------------

		ACLMessage request = new ACLMessage(ACLMessage.REQUEST);
		request.setLanguage(new SLCodec().getName());
		request.setOntology(MobilityOntology.getInstance().getName());
		try {
			getContentManager().fillContent(request, action);
			request.addReceiver(action.getActor());
			send(request);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}